package tree

import (
	
	
)

//XMLSpace is the W3C XML namespace
const XMLSpace = "http://www.w3.org/XML/1998/namespace"

//NodePos is a helper for representing the node's document order
type NodePos int

//Pos returns the node's document order position
func ( NodePos) () int {
	return int()
}

//NodeType is a safer way to determine a node's type than type assertions.
type NodeType int

//GetNodeType returns the node's type.
func ( NodeType) () NodeType {
	return 
}

//These are all the possible node types
const (
	NtAttr NodeType = iota
	NtChd
	NtComm
	NtElem
	NtNs
	NtRoot
	NtPi
)

//Node is a XPath result that is a node except elements
type Node interface {
	//ResValue prints the node's string value
	ResValue() string
	//Pos returns the node's position in the document order
	Pos() int
	//GetToken returns the xml.Token representation of the node
	GetToken() xml.Token
	//GetParent returns the parent node, which will always be an XML element
	GetParent() Elem
	//GetNodeType returns the node's type
	GetNodeType() NodeType
}

//Elem is a XPath result that is an element node
type Elem interface {
	Node
	//GetChildren returns the elements children.
	GetChildren() []Node
	//GetAttrs returns the attributes of the element
	GetAttrs() []Node
}

//NSElem is a node that keeps track of namespaces.
type NSElem interface {
	Elem
	GetNS() map[xml.Name]string
}

//NSBuilder is a helper-struct for satisfying the NSElem interface
type NSBuilder struct {
	NS map[xml.Name]string
}

//GetNS returns the namespaces found on the current element.  It should not be
//confused with BuildNS, which actually resolves the namespace nodes.
func ( NSBuilder) () map[xml.Name]string {
	return .NS
}

type nsValueSort []NS

func ( nsValueSort) () int { return len() }
func ( nsValueSort) (,  int) {
	[], [] = [], []
}
func ( nsValueSort) (,  int) bool {
	return [].Value < [].Value
}

//BuildNS resolves all the namespace nodes of the element and returns them
func ( Elem) ( []NS) {
	 := make(map[xml.Name]string)

	if ,  := .(NSElem);  {
		buildNS(, )

		 = make([]NS, 0, len())
		 := 1

		for ,  := range  {
			if !(.Local == "xmlns" && .Space == "" &&  == "") {
				 = append(, NS{
					Attr:     xml.Attr{Name: , Value: },
					Parent:   ,
					NodeType: NtNs,
				})
				++
			}
		}

		sort.Sort(nsValueSort())
		for  := range  {
			[].NodePos = NodePos(.Pos() +  + 1)
		}
	}

	return 
}

func buildNS( NSElem,  map[xml.Name]string) {
	if .GetNodeType() == NtRoot {
		return
	}

	if ,  := .GetParent().(NSElem);  {
		(, )
	}

	for ,  := range .GetNS() {
		[] = 
	}
}

//NS is a namespace node.
type NS struct {
	xml.Attr
	Parent Elem
	NodePos
	NodeType
}

//GetToken returns the xml.Token representation of the namespace.
func ( NS) () xml.Token {
	return .Attr
}

//GetParent returns the parent node of the namespace.
func ( NS) () Elem {
	return .Parent
}

//ResValue returns the string value of the namespace
func ( NS) () string {
	return .Attr.Value
}

//GetAttribute is a convenience function for getting the specified attribute from an element.
//false is returned if the attribute is not found.
func ( Elem, ,  string) (xml.Attr, bool) {
	 := .GetAttrs()
	for ,  := range  {
		 := .GetToken().(xml.Attr)
		if  == .Name.Local &&  == .Name.Space {
			return , true
		}
	}
	return xml.Attr{}, false
}

//GetAttributeVal is like GetAttribute, except it returns the attribute's value.
func ( Elem, ,  string) (string, bool) {
	,  := GetAttribute(, , )
	return .Value, 
}

//GetAttrValOrEmpty is like GetAttributeVal, except it returns an empty string if
//the attribute is not found instead of false.
func ( Elem, ,  string) string {
	,  := GetAttributeVal(, , )
	if ! {
		return ""
	}
	return 
}

//FindNodeByPos finds a node from the given position.  Returns nil if the node
//is not found.
func ( Node,  int) Node {
	if .Pos() ==  {
		return 
	}

	if ,  := .(Elem);  {
		 := .GetChildren()
		for  := 1;  < len(); ++ {
			if [-1].Pos() <=  && [].Pos() >  {
				return ([-1], )
			}
		}

		if len() > 0 {
			if [len()-1].Pos() <=  {
				return ([len()-1], )
			}
		}

		 := .GetAttrs()
		for ,  := range  {
			if .Pos() ==  {
				return 
			}
		}

		 := BuildNS()
		for ,  := range  {
			if .Pos() ==  {
				return 
			}
		}
	}

	return nil
}